#ifndef CSUPBR_SHAPE_H
#define CSUPBR_SHAPE_H

#include <vector>
#include <memory>

#include "transform.h"

namespace csupbr {

    struct Intersection;

    struct Shape {
        virtual bool intersect(const Ray &ray, Intersection &isct) const = 0;
        virtual bool intersectP(const Ray &ray, bool blend_test) const = 0;

        virtual BBox3f getBBox() const = 0;
        virtual float getArea() const {return .0f;}
    };

    struct Sphere : Shape {
        Sphere(std::shared_ptr<Transform> o2w, float radius = 1.f)
            : o2w(o2w), w2o(o2w->inverseRef()), radius(radius) {}

        bool intersect(const Ray &ray, Intersection &isct) const override;
        bool intersectP(const Ray &ray, bool blend_test) const override;

        BBox3f getBBox() const override;
        float getArea() const override {return Pi4 * radius * radius; }

        float radius;
        std::shared_ptr<Transform> o2w;
        std::shared_ptr<Transform> w2o;
    };

    struct Triangle;

    struct TriangleMesh {
        std::vector<Point3f> vertices;
        std::vector<Normal> normals;
        std::vector<Vec2f> texCoords;
        std::vector<Vertex> faces;

        int vertices_num;
        int faces_num;

        TriangleMesh(std::shared_ptr<Transform> o2w,
                     int vertices_num,
                     int faces_num,
                     const Point3f *vertices,
                     const Normal *normals,
                     const Vec2f *texCoords,
                     const Vertex *faces);

        static std::vector<std::shared_ptr<Triangle>> Create(std::shared_ptr<Transform> o2w,
                                                    int vertices_num,
                                                    int faces_num,
                                                    const Point3f *vertices,
                                                    const Normal *normals,
                                                    const Vec2f *texCoords,
                                                    const Vertex *faces);
    };

    struct Triangle : Shape {
        Triangle(std::shared_ptr<TriangleMesh> triangle_mesh, int index)
            : mesh(triangle_mesh), id(index) {}

        bool intersect(const Ray &ray, Intersection &isct) const override;
        bool intersectP(const Ray &ray, bool blend_test) const override;
        
        BBox3f getBBox() const override;
        float getArea() const override {
            Point3f v_a, v_b, v_c;
            return .5f * cross(v_a - v_b, v_c - v_b).length();
        }

        std::shared_ptr<TriangleMesh> mesh;
        int id;

    };

    

}
#endif // !CSUPBR_SHAPE_H